#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <imp/imp_log.h>
#include <imp/imp_ivs.h>
#include <imp/imp_ivs_move.h>
#include <ivs_move.h>
#include <system/system.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define TAG "IMPIVSMOVE"

#define NUM_SMG		6
//#define TEST_BUF

static int MoveInit(IMPIVSInterface *inf)
{
	ivs_move_t *gmove = NULL;

	if (!inf || !inf->param) {
		IMP_LOG_ERR(TAG, "%s:param is NULL!\n", __func__);
		goto err_param_null;
	}

	gmove = imp_alloc_move((IMP_IVS_MoveParam *)inf->param, IMP_IVS_ReleaseData);
	if (gmove == NULL) {
		IMP_LOG_ERR(TAG, "imp_alloc_move() error !\n");
		goto err_imp_alloc_move;
	}

	gmove->movesem = calloc(1, sizeof(ivs_move_sem_t));
	if (NULL == gmove->movesem) {
		IMP_LOG_ERR(TAG, "calloc movesem error !\n");
		goto err_calloc_movesem;
	}

	gmove->movesem->result = calloc(NUM_SMG, sizeof(IMP_IVS_MoveOutput));
	if (gmove->movesem->result == NULL) {
		IMP_LOG_ERR(TAG, "calloc() movesem->result error !\n");
		goto err_calloc_movesem_result;
	}

	gmove->movesem->index_write = 0;
	gmove->movesem->index_read = 0;

	inf->priv = gmove;

	return 0;

err_calloc_movesem_result:
	free(gmove->movesem);
err_calloc_movesem:
	imp_free_move(gmove);
err_imp_alloc_move:
	inf->priv = NULL;
err_param_null:
	return -1;
}

static void MoveExit(IMPIVSInterface *inf)
{
	ivs_move_t *gmove = NULL;

	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL !\n", __func__);
		goto err_gmove_null;
	}

	gmove = inf->priv;

	if(gmove->movesem == NULL) {
		IMP_LOG_ERR(TAG, "gmove->movesem is NULL !\n");
		goto err_movesem_null;
	}

	if(gmove->movesem->result == NULL) {
		IMP_LOG_ERR(TAG, "gmove->movesem->result is NULL !\n");
		goto err_movesem_result_null;
	}

	free(gmove->movesem->result);
	free(gmove->movesem);
	imp_free_move(gmove);

	inf->priv = NULL;

	return;

err_movesem_result_null:
	free(gmove->movesem);
err_movesem_null:
	imp_free_move(gmove);
	inf->priv = NULL;
err_gmove_null:
	return;
}

static int MovePreprocessSync(IMPIVSInterface *inf, IMPFrameInfo *frame)
{
	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL!\n", __func__);
		return -1;
	}

	return imp_move_preprocess(inf->priv, frame);
}

static int MoveProcessAsync(IMPIVSInterface *inf, IMPFrameInfo *frame)
{
	int id, ret;

	ivs_move_t *gmove = NULL;

#ifdef TEST_BUF
	int fd;

	fd = open("./yuyv", O_RDWR | O_CREAT | O_APPEND, 0777);
	if(fd < 0) {
		IMP_LOG_ERR(TAG, "fd error !\n");
		return -1;
	}

	write(fd, (void *)frame->virAddr, frame->width * frame->height);

	close(fd);
#endif

	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL!\n", __func__);
		return -1;
	}

	gmove = (ivs_move_t *)inf->priv;

	id = gmove->movesem->index_write;

	ret = imp_move_process(gmove, frame, &(gmove->movesem->result[id]));
	if (ret == 0) {
		gmove->movesem->index_write = ((gmove->movesem->index_write + 1) % NUM_SMG);
	} else if (ret < 0) {
		IMP_LOG_ERR(TAG, "imp_move_process() error !\n");
	}

	return ret;
}

static int MoveGetResult(IMPIVSInterface *inf, void **result)
{
	int id;
	ivs_move_t *gmove = NULL;

	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL !\n", __func__);
		return -1;
	}

	gmove = (ivs_move_t *)inf->priv;

	id = gmove->movesem->index_read;

	*result = &(gmove->movesem->result[id]);

	gmove->movesem->index_read = ((gmove->movesem->index_read + 1) % NUM_SMG);

	return 0;
}

static int MoveReleaseResult(IMPIVSInterface *inf, void *result)
{
	return 0;
}

static int MoveGetParam(IMPIVSInterface *inf, void *param)
{
	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL !\n", __func__);
		return -1;
	}

	return imp_get_move_param(inf->priv, param);
}

static int MoveSetParam(IMPIVSInterface *inf, void *param)
{
	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL !\n", __func__);
		return -1;
	}

	return imp_set_move_param(inf->priv, param);
}

static int MoveFlushFrame(IMPIVSInterface *inf)
{
	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL !\n", __func__);
		return -1;
	}

	return imp_flush_frame(inf->priv);
}

IMPIVSInterface *IMP_IVS_CreateMoveInterface(IMP_IVS_MoveParam *param)
{
	IMPIVSInterface *moveInterface = calloc(1, sizeof(IMPIVSInterface) + sizeof(IMP_IVS_MoveParam));
	if (NULL == moveInterface) {
		IMP_LOG_ERR(TAG, "calloc moveInterface is NULL!\n");
		return NULL;
	}

	moveInterface->param		= (char *)moveInterface + sizeof(IMPIVSInterface);
	memcpy(moveInterface->param, param, sizeof(IMP_IVS_MoveParam));
	moveInterface->paramSize	= sizeof(IMP_IVS_MoveParam);
	moveInterface->pixfmt		= PIX_FMT_NV12;
	moveInterface->init			= MoveInit;
	moveInterface->exit			= MoveExit;
	moveInterface->preProcessSync = MovePreprocessSync;
	moveInterface->processAsync	= MoveProcessAsync;
	moveInterface->getResult	= MoveGetResult;
	moveInterface->releaseResult= MoveReleaseResult;
	moveInterface->getParam		= MoveGetParam;
	moveInterface->setParam		= MoveSetParam;
	moveInterface->flushFrame	= MoveFlushFrame;

	return moveInterface;
}

void IMP_IVS_DestroyMoveInterface(IMPIVSInterface *moveInterface)
{
	if (NULL != moveInterface) {
		free(moveInterface);
	}
}
